home *** CD-ROM | disk | FTP | other *** search
/ Almathera Ten Pack 3: CDPD 3 / Almathera Ten on Ten - Disc 3: CDPD3.iso / fish / 001-100 / 001-025 / 004 / grep / grep.c < prev    next >
C/C++ Source or Header  |  1995-03-17  |  14KB  |  561 lines

  1.  /*
  2.  *
  3.  *
  4.  * The    information  in  this  document  is  subject  to  change
  5.  * without  notice  and  should not be construed as a commitment
  6.  * by Digital Equipment Corporation or by DECUS.
  7.  *
  8.  * Neither Digital Equipment Corporation, DECUS, nor the authors
  9.  * assume any responsibility for the use or reliability of  this
  10.  * document or the described software.
  11.  *
  12.  *    Copyright (C) 1980, DECUS
  13.  *
  14.  *
  15.  * General permission to copy or modify, but not for profit,  is
  16.  * hereby  granted,  provided that the above copyright notice is
  17.  * included and reference made to  the    fact  that  reproduction
  18.  * privileges were granted by DECUS.
  19.  *
  20.  */
  21. #include <stdio.h>
  22. #include <ctype.h>
  23.  /*
  24.  * grep.
  25.  *
  26.  * Runs on the Decus compiler or on vms.
  27.  * Converted for BDS compiler (under CP/M-80), 20-Jan-83, by Chris Kern.
  28.  *
  29.  * Converted to IBM PC with CI-C86 C Compiler June 1983 by David N. Smith
  30.  *
  31.  * On vms, define as:
  32.  *
  33.  *    grep :== "$disk:[account]grep"     (native)
  34.  *    grep :== "$disk:[account]grep grep"     (Decus)
  35.  *
  36.  * See below for more information.
  37.  *
  38.  */
  39. char    *documentation[] = {
  40. "grep searches a file for a given pattern.  Execute by",
  41. "   grep [flags] regular_expression file_list",
  42. "",
  43. "Flags are single characters preceeded by '-':",
  44. "   -c      Only a count of matching lines is printed",
  45. "   -f      Print file name for matching lines switch, see below",
  46. "   -n      Each line is preceeded by its line number",
  47. "   -v      Only print non-matching lines",
  48. "",
  49. "The file_list is a list of files (wildcards are acceptable on RSX modes).",
  50. "",
  51. "The file name is normally printed if there is a file given.",
  52. "The -f flag reverses this action (print name no file, not if more).",
  53. "",
  54. 0 };
  55. char    *patdoc[] = {
  56. "The regular_expression defines the pattern to search for.  Upper- and",
  57. "lower-case are always ignored.  Blank lines never match.  The expression",
  58. "should be quoted to prevent file-name translation.",
  59. "x      An ordinary character (not mentioned below) matches that character.",
  60. "'\\'    The backslash quotes any character.  \"\\$\" matches a dollar-sign.",
  61. "'^'    A circumflex at the beginning of an expression matches the",
  62. "       beginning of a line.",
  63. "'$'    A dollar-sign at the end of an expression matches the end of a line.",
  64. "'.'    A period matches any character except \"new-line\".",
  65. "':a'   A colon matches a class of characters described by the following",
  66. "':d'     character.  \":a\" matches any alphabetic, \":d\" matches digits,",
  67. "':n'     \":n\" matches alphanumerics, \": \" matches spaces, tabs, and",
  68. "': '     other control characters, such as new-line.",
  69. "'*'    An expression followed by an asterisk matches zero or more",
  70. "       occurrances of that expression: \"fo*\" matches \"f\", \"fo\"",
  71. "       \"foo\", etc.",
  72. "'+'    An expression followed by a plus sign matches one or more",
  73. "       occurrances of that expression: \"fo+\" matches \"fo\", etc.",
  74. "'-'    An expression followed by a minus sign optionally matches",
  75. "       the expression.",
  76. "'[]'   A string enclosed in square brackets matches any character in",
  77. "       that string, but no others.  If the first character in the",
  78. "       string is a circumflex, the expression matches any character",
  79. "       except \"new-line\" and the characters in the string.  For",
  80. "       example, \"[xyz]\" matches \"xx\" and \"zyx\", while \"[^xyz]\"",
  81. "       matches \"abc\" but not \"axb\".  A range of characters may be",
  82. "       specified by two characters separated by \"-\".  Note that,",
  83. "       [a-z] matches alphabetics, while [z-a] never matches.",
  84. "The concatenation of regular expressions is a regular expression.",
  85. 0};
  86. /*$ifdef  vms                     */
  87. /*$define VMS            VMS native compiler  */
  88. /*$define error(s)   _error(s)             */
  89. /*$endif                     */
  90. #define LMAX    512
  91. #define PMAX    256
  92. #define CHAR    1
  93. #define BOL    2
  94. #define EOL    3
  95. #define ANY    4
  96. #define CLASS    5
  97. #define NCLASS    6
  98. #define STAR    7
  99. #define PLUS    8
  100. #define MINUS    9
  101. #define ALPHA    10
  102. #define DIGIT    11
  103. #define NALPHA    12
  104. #define PUNCT    13
  105. #define RANGE    14
  106. #define ENDPAT    15
  107. int    cflag;
  108. int    fflag;
  109. int    nflag;
  110. int    vflag;
  111. int    nfile;
  112. int    debug    =    0;       /* Set for debug code      */
  113. char    *pp;
  114. #ifndef vms
  115. char    file_name[81];
  116. #endif
  117. char    lbuf[LMAX];
  118. char    pbuf[PMAX];
  119. /*******************************************************/
  120. main(argc, argv)
  121. char *argv[];
  122. {
  123.    register char   *p;
  124.    register int    c, i;
  125.    int           gotpattern;
  126.    FILE        *f;
  127.    if (argc <= 1)
  128.       usage("No arguments");
  129.    if (argc == 2 && argv[1][0] == '?' && argv[1][1] == 0) {
  130.       help(documentation);
  131.       help(patdoc);
  132.       return (0);
  133.       }
  134.    nfile = argc-1;
  135.    gotpattern = 0;
  136.    for (i=1; i < argc; ++i) {
  137.       p = argv[i];
  138.       if (*p == '-') {
  139.      ++p;
  140.      while (c = *p++) {
  141.         switch(tolower(c)) {
  142.         case '?':
  143.            help(documentation);
  144.            break;
  145.         case 'C':
  146.         case 'c':
  147.            ++cflag;
  148.            break;
  149.         case 'D':
  150.         case 'd':
  151.            ++debug;
  152.            break;
  153.         case 'F':
  154.         case 'f':
  155.            ++fflag;
  156.            break;
  157.         case 'n':
  158.         case 'N':
  159.            ++nflag;
  160.            break;
  161.         case 'v':
  162.         case 'V':
  163.            ++vflag;
  164.            break;
  165.         default:
  166.            usage("Unknown flag");
  167.         }
  168.      }
  169.      argv[i] = 0;
  170.      --nfile;
  171.       } else if (!gotpattern) {
  172.      compile(p);
  173.      argv[i] = 0;
  174.      ++gotpattern;
  175.      --nfile;
  176.       }
  177.    }
  178.    if (!gotpattern)
  179.       usage("No pattern");
  180.    if (nfile == 0)
  181.       grep(stdin, 0);
  182.    else {
  183.       fflag = fflag ^ (nfile > 0);
  184.       for (i=1; i < argc; ++i) {
  185.      if (p = argv[i]) {
  186.         if ((f=fopen(p, "r")) == NULL)
  187.            cant(p);
  188.         else {
  189.            grep(f, p);
  190.            fclose(f);
  191.         }
  192.      }
  193.       }
  194.    }
  195.    return (0);
  196. }
  197. /*******************************************************/
  198. file(s)
  199. char *s;
  200. {
  201.    printf("File %s:\n", s);
  202. }
  203. /*******************************************************/
  204. cant(s)
  205. char *s;
  206. {
  207.    fprintf(stderr, "%s: cannot open\n", s);
  208. }
  209. /*******************************************************/
  210. help(hp)
  211. char **hp;
  212. /*
  213.  * Give good help
  214.  */
  215. {
  216.    register char   **dp;
  217.    for (dp = hp; *dp; dp++)
  218.       printf("%s\n", *dp);
  219. }
  220. /*******************************************************/
  221. usage(s)
  222. char    *s;
  223. {
  224.    fprintf(stderr, "?GREP-E-%s\n", s);
  225.    fprintf(stderr,
  226.       "Usage: grep [-cfnv] pattern [file ...].  grep ? for help\n");
  227.    exit(1);
  228. }
  229. /*******************************************************/
  230. compile(source)
  231. char       *source;   /* Pattern to compile        */
  232. /*
  233.  * Compile the pattern into global pbuf[]
  234.  */
  235. {
  236.    register char  *s;          /* Source string pointer       */
  237.    register char  *lp;          /* Last pattern pointer       */
  238.    register int   c;          /* Current character       */
  239.    int          o;          /* Temp               */
  240.    char       *spp;       /* Save beginning of pattern */
  241.    char       *cclass();  /* Compile class routine       */
  242.    s = source;
  243.    if (debug)
  244.       printf("Pattern = \"%s\"\n", s);
  245.    lp = pp = pbuf;
  246.    while (c = *s++) {
  247.       /*
  248.        * STAR, PLUS and MINUS are special.
  249.        */
  250.       if (c == '*' || c == '+' || c == '-') {
  251.      if (pp == pbuf ||
  252.           (o=pp[-1]) == BOL ||
  253.           o == EOL ||
  254.           o == STAR ||
  255.           o == PLUS ||
  256.           o == MINUS)
  257.         badpat("Illegal occurrance op.", source, s);
  258.      store(ENDPAT);
  259.      store(ENDPAT);
  260.      spp = pp;         /* Save pattern end     */
  261.      while (--pp > lp)     /* Move pattern down     */
  262.         *pp = pp[-1];     /* one byte         */
  263.      *pp =     (c == '*') ? STAR :
  264.         (c == '-') ? MINUS : PLUS;
  265.      pp = spp;         /* Restore pattern end  */
  266.      continue;
  267.       }
  268.       /*
  269.        * All the rest.
  270.        */
  271.       lp = pp;           /* Remember start       */
  272.       switch(c) {
  273.       case '^':
  274.      store(BOL);
  275.      break;
  276.       case '$':
  277.      store(EOL);
  278.      break;
  279.       case '.':
  280.      store(ANY);
  281.      break;
  282.       case '[':
  283.      s = cclass(source, s);
  284.      break;
  285.       case ':':
  286.      if (*s) {
  287.         c = *s++;
  288.         switch(tolower(c)) {
  289.         case 'a':
  290.         case 'A':
  291.            store(ALPHA);
  292.            break;
  293.         case 'd':
  294.         case 'D':
  295.            store(DIGIT);
  296.            break;
  297.         case 'n':
  298.         case 'N':
  299.            store(NALPHA);
  300.            break;
  301.         case ' ':
  302.            store(PUNCT);
  303.            break;
  304.         default:
  305.            badpat("Unknown : type", source, s);
  306.         }
  307.         break;
  308.      }
  309.      else     badpat("No : type", source, s);
  310.       case '\\':
  311.      if (*s)
  312.         c = *s++;
  313.       default:
  314.      store(CHAR);
  315.      store(tolower(c));
  316.       }
  317.    }
  318.    store(ENDPAT);
  319.    store(0);            /* Terminate string     */
  320.    if (debug) {
  321.       for (lp = pbuf; lp < pp;) {
  322.      if ((c = (*lp++ & 0377)) < ' ')
  323.         printf("\\%o ", c);
  324.      else     printf("%c ", c);
  325.     }
  326.     printf("\n");
  327.    }
  328. }
  329. /*******************************************************/
  330. char *
  331. cclass(source, src)
  332. char       *source;   /* Pattern start -- for error msg.      */
  333. char       *src;      /* Class start           */
  334. /*
  335.  * Compile a class (within [])
  336.  */
  337. {
  338.    register char   *s;          /* Source pointer    */
  339.    register char   *cp;       /* Pattern start       */
  340.    register int    c;          /* Current character */
  341.    int           o;          /* Temp           */
  342.    s = src;
  343.    o = CLASS;
  344.    if (*s == '^') {
  345.       ++s;
  346.       o = NCLASS;
  347.    }
  348.    store(o);
  349.    cp = pp;
  350.    store(0);                  /* Byte count     */
  351.    while ((c = *s++) && c!=']') {
  352.       if (c == '\\') {                /* Store quoted char    */
  353.      if ((c = *s++) == '\0')      /* Gotta get something  */
  354.         badpat("Class terminates badly", source, s);
  355.      else     store(tolower(c));
  356.       }
  357.       else if (c == '-' &&
  358.         (pp - cp) > 1 && *s != ']' && *s != '\0') {
  359.      c = pp[-1];         /* Range start     */
  360.      pp[-1] = RANGE;     /* Range signal    */
  361.      store(c);         /* Re-store start  */
  362.      c = *s++;         /* Get end char and*/
  363.      store(tolower(c));     /* Store it        */
  364.       }
  365.       else {
  366.      store(tolower(c));     /* Store normal char */
  367.       }
  368.    }
  369.    if (c != ']')
  370.       badpat("Unterminated class", source, s);
  371.    if ((c = (pp - cp)) >= 256)
  372.       badpat("Class too large", source, s);
  373.    if (c == 0)
  374.       badpat("Empty class", source, s);
  375.    *cp = c;
  376.    return(s);
  377. }
  378. /*******************************************************/
  379. store(op)
  380. {
  381.    if (pp > &pbuf[PMAX-1])
  382.       error("Pattern too complex\n");
  383.    *pp++ = op;
  384. }
  385. /*******************************************************/
  386. badpat(message, source, stop)
  387. char  *message;       /* Error message */
  388. char  *source;          /* Pattern start */
  389. char  *stop;          /* Pattern end   */
  390. {
  391.    fprintf(stderr, "-GREP-E-%s, pattern is\"%s\"\n", message, source);
  392.    fprintf(stderr, "-GREP-E-Stopped at byte %d, '%c'\n",
  393.      stop-source, stop[-1]);
  394.    error("?GREP-E-Bad pattern\n");
  395. }
  396. /*******************************************************/
  397. grep(fp, fn)
  398. FILE       *fp;       /* File to process        */
  399. char       *fn;       /* File name (for -f option)  */
  400. /*
  401.  * Scan the file for the pattern in pbuf[]
  402.  */
  403. {
  404.    register int lno, count, m;
  405.    lno = 0;
  406.    count = 0;
  407.    while (fgets(lbuf, LMAX, fp)) {
  408.       ++lno;
  409.       m = match();
  410.       if ((m && !vflag) || (!m && vflag)) {
  411.      ++count;
  412.      if (!cflag) {
  413.         if (fflag && fn) {
  414.            file(fn);
  415.            fn = 0;
  416.         }
  417.         if (nflag)
  418.            printf("%d\t", lno);
  419.         printf("%s\n", lbuf);
  420.      }
  421.       }
  422.    }
  423.    if (cflag) {
  424.       if (fflag && fn)
  425.      file(fn);
  426.       printf("%d\n", count);
  427.    }
  428. }
  429. /*******************************************************/
  430. match()
  431. /*
  432.  * Match the current line (in lbuf[]), return 1 if it does.
  433.  */
  434. {
  435.    register char   *l;          /* Line pointer        */
  436.    char *pmatch();
  437.    for (l = lbuf; *l; l++) {
  438.       if (pmatch(l, pbuf))
  439.      return(1);
  440.    }
  441.    return(0);
  442. }
  443. /*******************************************************/
  444. char *
  445. pmatch(line, pattern)
  446. char           *line;     /* (partial) line to match      */
  447. char           *pattern;  /* (partial) pattern to match   */
  448. {
  449.    register char   *l;          /* Current line pointer          */
  450.    register char   *p;          /* Current pattern pointer      */
  451.    register char   c;          /* Current character          */
  452.    char        *e;          /* End for STAR and PLUS match  */
  453.    int           op;          /* Pattern operation          */
  454.    int           n;          /* Class counter              */
  455.    char        *are;      /* Start of STAR match          */
  456.    l = line;
  457.    if (debug > 1)
  458.       printf("pmatch(\"%s\")\n", line);
  459.    p = pattern;
  460.    while ((op = *p++) != ENDPAT) {
  461.       if (debug > 1)
  462.      printf("byte[%d] = 0%o, '%c', op = 0%o\n",
  463.            l-line, *l, *l, op);
  464.       switch(op) {
  465.       case CHAR:
  466.      if (tolower(*l) != *p++)
  467.         return(0);
  468.      l++;
  469.      break;
  470.       case BOL:
  471.      if (l != lbuf)
  472.         return(0);
  473.      break;
  474.       case EOL:
  475.      if (*l != '\0')
  476.         return(0);
  477.      break;
  478.       case ANY:
  479.      if (*l++ == '\0')
  480.         return(0);
  481.      break;
  482.       case DIGIT:
  483.      if ((c = *l++) < '0' || (c > '9'))
  484.         return(0);
  485.      break;
  486.       case ALPHA:
  487.      c = tolower(*l);
  488.      l++;
  489.      if (c < 'a' || c > 'z')
  490.         return(0);
  491.      break;
  492.       case NALPHA:
  493.      c = tolower(*l);
  494.      l++;
  495.      if (c >= 'a' && c <= 'z')
  496.         break;
  497.      else if (c < '0' || c > '9')
  498.         return(0);
  499.      break;
  500.       case PUNCT:
  501.      c = *l++;
  502.      if (c == 0 || c > ' ')
  503.         return(0);
  504.      break;
  505.       case CLASS:
  506.       case NCLASS:
  507.      c = tolower(*l);
  508.      l++;
  509.      n = *p++ & 0377;
  510.      do {
  511.         if (*p == RANGE) {
  512.            p += 3;
  513.            n -= 2;
  514.            if (c >= p[-2] && c <= p[-1])
  515.           break;
  516.         }
  517.         else if (c == *p++)
  518.            break;
  519.      } while (--n > 1);
  520.      if ((op == CLASS) == (n <= 1))
  521.         return(0);
  522.      if (op == CLASS)
  523.         p += n - 2;
  524.      break;
  525.       case MINUS:
  526.      e = pmatch(l, p);     /* Look for a match    */
  527.      while (*p++ != ENDPAT); /* Skip over pattern    */
  528.      if (e)          /* Got a match?    */
  529.         l = e;         /* Yes, update string    */
  530.      break;          /* Always succeeds    */
  531.       case PLUS:         /* One or more ...    */
  532.      if ((l = pmatch(l, p)) == 0)
  533.         return(0);         /* Gotta have a match    */
  534.       case STAR:         /* Zero or more ...    */
  535.      are = l;         /* Remember line start */
  536.      while (*l && (e = pmatch(l, p)))
  537.         l = e;         /* Get longest match    */
  538.      while (*p++ != ENDPAT); /* Skip over pattern    */
  539.      while (l >= are) {     /* Try to match rest    */
  540.         if (e = pmatch(l, p))
  541.            return(e);
  542.         --l;         /* Nope, try earlier    */
  543.      }
  544.      return(0);         /* Nothing else worked */
  545.       default:
  546.      printf("Bad op code %d\n", op);
  547.      error("Cannot happen -- match\n");
  548.       }
  549.    }
  550.    return(l);
  551. }
  552. /*******************************************************/
  553. error(s)
  554. char *s;
  555. {
  556.    fprintf(stderr, "%s", s);
  557.    exit(1);
  558. }
  559.  
  560.  
  561.